package com.elitesland.system.service.impl;

import com.elitesland.system.convert.SysPermissionConvert;
import com.elitesland.system.convert.SysRoleConvert;
import com.elitesland.system.entity.*;
import com.elitesland.system.repo.SysRolePermissionRepo;
import com.elitesland.system.service.SysPermissionService;
import com.elitesland.system.service.SysRolePermissionService;
import com.elitesland.system.vo.SysPermissionVO;
import com.elitesland.system.vo.SysRoleVO;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <pre>
 *     角色权限关联服务实现
 * </pre>
 *
 * @author mir
 * @date 2020-09-28
 */
@Service
public class SysRolePermissionServiceImpl implements SysRolePermissionService {

    private SysRolePermissionRepo sysRolePermissionRepo;

    private SysPermissionService sysPermissionService;

    private JPAQueryFactory jpaQueryFactory;

    @Autowired
    public void setSysRolePermissionRepo(SysRolePermissionRepo sysRolePermissionRepo) {
        this.sysRolePermissionRepo = sysRolePermissionRepo;
    }

    @Autowired
    public void setSysPermissionService(SysPermissionService sysPermissionService) {
        this.sysPermissionService = sysPermissionService;
    }

    @Autowired
    public void setJpaQueryFactory(JPAQueryFactory jpaQueryFactory) {
        this.jpaQueryFactory = jpaQueryFactory;
    }

    private final QSysRolePermissionDO rolePermissions = QSysRolePermissionDO.sysRolePermissionDO;

    private final QSysRoleDO roles = QSysRoleDO.sysRoleDO;

    private final QSysPermissionDO permissions = QSysPermissionDO.sysPermissionDO;

    @Override
    public Set<SysPermissionVO> listPermissionsByRoleId(Long roleId) {
        return jpaQueryFactory.select(permissions)
                .from(permissions)
                .leftJoin(rolePermissions).on(rolePermissions.permissionId.eq(permissions.id))
                .where(rolePermissions.roleId.eq(roleId))
                .fetch()
                .stream().map(SysPermissionConvert.INSTANCE::doToVO)
                .collect(Collectors.toSet());
    }

    @Override
    public Set<SysPermissionVO> listMenusByRoleId(Long roleId) {
        return jpaQueryFactory.select(permissions)
                .from(permissions)
                .leftJoin(rolePermissions).on(rolePermissions.permissionId.eq(permissions.id))
                .where(rolePermissions.roleId.eq(roleId).and(permissions.permType.eq(0)))
                .fetch()
                .stream().map(SysPermissionConvert.INSTANCE::doToVO)
                .collect(Collectors.toSet());
    }

    @Override
    public Set<SysPermissionVO> listActionsByRoleId(Long roleId) {
        return jpaQueryFactory.select(permissions)
                .from(permissions)
                .leftJoin(rolePermissions).on(rolePermissions.permissionId.eq(permissions.id))
                .where(rolePermissions.roleId.eq(roleId).and(permissions.permType.eq(1)))
                .fetch()
                .stream().map(SysPermissionConvert.INSTANCE::doToVO)
                .collect(Collectors.toSet());
    }

    @Override
    public Map<Long, Set<SysPermissionVO>> listPermissionsByRoleIds(List<Long> roleIds) {
        val rolePermissionTuples = jpaQueryFactory.select(
                rolePermissions.roleId,
                permissions
        ).from(permissions)
                .leftJoin(rolePermissions).on(rolePermissions.permissionId.eq(permissions.id))
                .where(rolePermissions.roleId.in(roleIds)).fetch();

        return rolePermissionTuples.stream().collect(
                Collectors.groupingBy(
                        t -> t.get(0, Long.class),
                        Collectors.mapping(
                                t -> SysPermissionConvert.INSTANCE.doToVO(t.get(1, SysPermissionDO.class)),
                                Collectors.toSet()
                        )
                )
        );
    }

    @Override
    public Set<SysRoleVO> listRolesByPermissionId(Long permissionId) {
        return jpaQueryFactory.select(roles)
                .from(roles)
                .leftJoin(rolePermissions).on(rolePermissions.roleId.eq(roles.id))
                .where(rolePermissions.permissionId.eq(permissionId))
                .fetch()
                .stream().map(SysRoleConvert.INSTANCE::doToVo)
                .collect(Collectors.toSet());
    }

    @Override
    public Map<Long, Set<SysRoleVO>> listRolesByPermissionIds(List<Long> permissionIds) {
        val permissionRoleTuples = jpaQueryFactory.select(
                rolePermissions.permissionId,
                roles
        ).from(roles)
                .leftJoin(rolePermissions).on(rolePermissions.roleId.eq(roles.id))
                .where(rolePermissions.permissionId.in(permissionIds)).fetch();

        return permissionRoleTuples.stream().collect(
                Collectors.groupingBy(
                        t -> t.get(0, Long.class),
                        Collectors.mapping(
                                t -> SysRoleConvert.INSTANCE.doToVo(t.get(1, SysRoleDO.class)),
                                Collectors.toSet()
                        )
                )
        );
    }

    @Override
    public Set<SysPermissionVO> listActionsByMenuIdOfRole(Long roleId, Long menuId) {
        val permDOs = jpaQueryFactory.select(permissions)
                .from(permissions)
                .leftJoin(rolePermissions).on(permissions.id.eq(rolePermissions.permissionId))
                .where(rolePermissions.roleId.eq(roleId).and(permissions.parentId.eq(menuId))).fetch();
        return permDOs.stream().map(SysPermissionConvert.INSTANCE::doToVO)
                .collect(Collectors.toSet());
    }

    @Override
    @Transactional(rollbackOn = {Exception.class})
    public void saveAll(List<SysRolePermissionDO> rps) {
        sysRolePermissionRepo.saveAll(rps);
    }

    @Override
    @Transactional(rollbackOn = {Exception.class})
    public void saveAllByMenu(Long roleId, Long menuId, Set<Long> permissionIds) {
        val permissions = sysPermissionService.listActionsByMenuId(menuId);
        val permIds = permissions.stream().map(SysPermissionVO::getId).collect(Collectors.toList());

        sysRolePermissionRepo.deleteAllByRoleIdAndPermissionIdIn(roleId, permIds);

        sysRolePermissionRepo.saveAll(permissionIds.stream().map(permid -> {
            return new SysRolePermissionDO()
                    .setRoleId(roleId)
                    .setPermissionId(permid);
        }).collect(Collectors.toList()));
    }

    @Override
    public void removePermissionsByRoleIds(List<Long> roleIds) {
        sysRolePermissionRepo.deleteAllByRoleIdIn(roleIds);
    }

    @Override
    public void removeRolesByPermissionIds(List<Long> permissionIds) {
        sysRolePermissionRepo.deleteAllByPermissionIdIn(permissionIds);
    }
}
